{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "This is to test TTS tensorflow models with benchmark sentences.\n",
    "\n",
    "Before running this script please DON'T FORGET: \n",
    "- to set file paths.\n",
    "- to download related models.\n",
    "    - Sample TF model: https://www.dropbox.com/sh/3b1fat5oxqab6yn/AADDlNs-9-r7ASbVnFYx3RHHa?dl=0\n",
    "- download or clone related repos, linked below.\n",
    "- setup the repositories. ```python setup.py install```\n",
    "- to checkout right commit versions (given next to the model in the models page).\n",
    "- to set the file paths below.\n",
    "\n",
    "Repositories:\n",
    "- TTS: https://github.com/mozilla/TTS\n",
    "- PWGAN: https://github.com/erogol/ParallelWaveGAN (if you like to use a vocoder model)\n",
    "\n",
    "Known Issues:\n",
    "- To load the model second time you need to restart the notebook kernel. \n",
    "- Some of the advance methods are not yet implemented for Tensorflow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false",
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "import os\n",
    "\n",
    "# you may need to change this depending on your system\n",
    "os.environ['CUDA_VISIBLE_DEVICES']='1'\n",
    "\n",
    "import sys\n",
    "import io\n",
    "import torch \n",
    "import tensorflow as tf\n",
    "print(tf.config.list_physical_devices('GPU'))\n",
    "\n",
    "import time\n",
    "import json\n",
    "import yaml\n",
    "import numpy as np\n",
    "from collections import OrderedDict\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams[\"figure.figsize\"] = (16,5)\n",
    "\n",
    "import librosa\n",
    "import librosa.display\n",
    "\n",
    "from TTS.tf.models.tacotron2 import Tacotron2\n",
    "from TTS.tf.utils.generic_utils import setup_model, load_checkpoint\n",
    "from TTS.utils.audio import AudioProcessor\n",
    "from TTS.utils.io import load_config\n",
    "from TTS.utils.synthesis import synthesis\n",
    "from TTS.utils.visual import visualize\n",
    "\n",
    "import IPython\n",
    "from IPython.display import Audio\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "def tts(model, text, CONFIG, use_cuda, ap, use_gl, figures=True):\n",
    "    t_1 = time.time()\n",
    "    waveform, alignment, mel_spec, mel_postnet_spec, stop_tokens, inputs = synthesis(model, text, CONFIG, use_cuda, ap, None, None, False, CONFIG.enable_eos_bos_chars, use_gl, backend=BACKEND)\n",
    "    if CONFIG.model == \"Tacotron\" and not use_gl:\n",
    "        # coorect the normalization differences b/w TTS and the Vocoder.\n",
    "        mel_postnet_spec = ap.out_linear_to_mel(mel_postnet_spec.T).T\n",
    "    print(mel_postnet_spec.shape)\n",
    "    print(\"max- \", mel_postnet_spec.max(), \" -- min- \", mel_postnet_spec.min())\n",
    "    if not use_gl:\n",
    "        waveform = vocoder_model.inference(torch.FloatTensor(mel_postnet_spec.T).unsqueeze(0))\n",
    "    mel_postnet_spec = ap._denormalize(mel_postnet_spec.T).T\n",
    "    if use_cuda and not use_gl:\n",
    "        waveform = waveform.cpu()\n",
    "        waveform = waveform.numpy()\n",
    "    waveform = waveform.squeeze()\n",
    "    rtf = (time.time() - t_1) / (len(waveform) / ap.sample_rate)\n",
    "    print(waveform.shape)\n",
    "    print(\" > Run-time: {}\".format(time.time() - t_1))\n",
    "    print(\" > Real-time factor: {}\".format(rtf))\n",
    "    if figures:  \n",
    "        visualize(alignment, mel_postnet_spec, stop_tokens, text, ap.hop_length, CONFIG, ap._denormalize(mel_spec.T).T)                                                                       \n",
    "    IPython.display.display(Audio(waveform, rate=CONFIG.audio['sample_rate'], normalize=True))  \n",
    "    os.makedirs(OUT_FOLDER, exist_ok=True)\n",
    "    file_name = text.replace(\" \", \"_\").replace(\".\",\"\") + \".wav\"\n",
    "    out_path = os.path.join(OUT_FOLDER, file_name)\n",
    "    ap.save_wav(waveform, out_path)\n",
    "    return alignment, mel_postnet_spec, stop_tokens, waveform"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "# Set constants\n",
    "ROOT_PATH = '../torch_model/'\n",
    "MODEL_PATH = ROOT_PATH + '/tts_tf_checkpoint_360000.pkl'\n",
    "CONFIG_PATH = ROOT_PATH + '/config.json'\n",
    "OUT_FOLDER = '/home/erogol/Dropbox/AudioSamples/benchmark_samples/'\n",
    "CONFIG = load_config(CONFIG_PATH)\n",
    "# Run FLAGs\n",
    "use_cuda = True  # use the available GPU (only for torch)\n",
    "# Set the vocoder\n",
    "use_gl = True  # use GL if True\n",
    "BACKEND = 'tf'  # set the backend for inference "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false",
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from TTS.utils.text.symbols import symbols, phonemes, make_symbols\n",
    "from TTS.tf.utils.convert_torch_to_tf_utils import tf_create_dummy_inputs\n",
    "c = CONFIG\n",
    "num_speakers = 0\n",
    "r = 1\n",
    "num_chars = len(phonemes) if c.use_phonemes else len(symbols)\n",
    "model = setup_model(num_chars, num_speakers, c)\n",
    "\n",
    "# before loading weights you need to run the model once to generate the variables\n",
    "input_ids, input_lengths, mel_outputs, mel_lengths = tf_create_dummy_inputs()\n",
    "mel_pred = model(input_ids, training=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false",
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "model = load_checkpoint(model, MODEL_PATH)\n",
    "# model = tf.function(model, experimental_relax_shapes=True)\n",
    "ap = AudioProcessor(**CONFIG.audio)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "# wrapper class to use tf.function\n",
    "class ModelInference(tf.keras.Model):\n",
    "    def __init__(self, model):\n",
    "        super(ModelInference, self).__init__()\n",
    "        self.model = model\n",
    "    \n",
    "    @tf.function(input_signature=[tf.TensorSpec(shape=(None, None), dtype=tf.int32)])\n",
    "    def call(self, characters):\n",
    "        return self.model(characters, training=False)\n",
    "    \n",
    "model = ModelInference(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "# LOAD WAVERNN\n",
    "if use_gl == False:\n",
    "    from parallel_wavegan.models import ParallelWaveGANGenerator, MelGANGenerator\n",
    "    \n",
    "    vocoder_model = MelGANGenerator(**VOCODER_CONFIG[\"generator_params\"])\n",
    "    vocoder_model.load_state_dict(torch.load(VOCODER_MODEL_PATH, map_location=\"cpu\")[\"model\"][\"generator\"])\n",
    "    vocoder_model.remove_weight_norm()\n",
    "    ap_vocoder = AudioProcessor(**VOCODER_CONFIG['audio'])    \n",
    "    if use_cuda:\n",
    "        vocoder_model.cuda()\n",
    "    vocoder_model.eval();\n",
    "    print(count_parameters(vocoder_model))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### Comparision with https://mycroft.ai/blog/available-voices/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence =  \"Bill got in the habit of asking himself “Is that thought true?” and if he wasn’t absolutely certain it was, he just let it go.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### https://espnet.github.io/icassp2020-tts/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"The Commission also recommends\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"As a result of these studies, the planning document submitted by the Secretary of the Treasury to the Bureau of the Budget on August thirty-one.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"The FBI now transmits information on all defectors, a category which would, of course, have included Oswald.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"they seem unduly restrictive in continuing to require some manifestation of animus against a Government official.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"and each agency given clear understanding of the assistance which the Secret Service expects.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### Other examples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Be a voice, not an echo.\"  # 'echo' is not in training set. \n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"It took me quite a long time to develop a voice, and now that I have it I'm not going to be silent.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"The human voice is the most perfect instrument of all.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"I'm sorry Dave. I'm afraid I can't do that.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"This cake is great. It's so delicious and moist.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### Comparison with https://keithito.github.io/audio-samples/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Generative adversarial network or variational auto-encoder.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Scientists at the CERN laboratory say they have discovered a new particle.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Here’s a way to measure the acute emotional intelligence that has never gone out of style.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"President Trump met with other leaders at the Group of 20 conference.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"The buses aren't the problem, they actually provide a solution.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### Comparison with https://google.github.io/tacotron/publications/tacotron/index.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Generative adversarial network or variational auto-encoder.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Basilar membrane and otolaryngology are not auto-correlations.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \" He has read the whole thing.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"He reads books.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Thisss isrealy awhsome.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"This is your internet browser, Firefox.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"This is your internet browser Firefox.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"The quick brown fox jumps over the lazy dog.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Does the quick brown fox jump over the lazy dog?\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Eren, how are you?\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "Collapsed": "false"
   },
   "source": [
    "### Hard Sentences"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Encouraged, he started with a minute a day.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"His meditation consisted of “body scanning” which involved focusing his mind and energy on each section of the body from head to toe .\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Recent research at Harvard has shown meditating for as little as 8 weeks can actually increase the grey matter in the parts of the brain responsible for emotional regulation and learning . \"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"If he decided to watch TV he really watched it.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "sentence = \"Often we try to bring about change through sheer effort and we put all of our energy into a new initiative .\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "# for twb dataset\n",
    "sentence = \"In our preparation for Easter, God in his providence offers us each year the season of Lent as a sacramental sign of our conversion.\"\n",
    "align, spec, stop_tokens, wav = tts(model, sentence, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "wavs = []\n",
    "model.eval()\n",
    "model.decoder.prenet.eval()\n",
    "model.decoder.max_decoder_steps = 2000\n",
    "# model.decoder.prenet.train()\n",
    "speaker_id = None\n",
    "sentence =  '''This is App Store Optimization report.\n",
    "The first tab on the report is App Details. App details report is updated weekly and Datetime column shows the latest report update date. The widget displays the app icon, respective app version, visual assets on the store, app description, latest app update date on the Appstore/Google PlayStore and what’s new section.\n",
    "In App Details tab, you can see not only your app but all Delivery Hero apps since we think it can be inspiring to see the other apps, their description and screenshots. \n",
    "Product name is the actual app name on the AppStore or Google Play Store.\n",
    "Screenshot URLs column display the actual screenshots on the store for the current version. No resizing is done. If you  click on the screenshot, you can see it in full-size.\n",
    "Current release date show the latest app update date when the query is run. Here we see that Appetito24 Android is updated to app version 4.6.3.2 on 28th of March.\n",
    "If the description is too long, clarisights is not able to display the full description; however, if you select description and current_release_date cells to copy and paste it to a text editor, you'll see the full description.\n",
    "If you scroll down in the widget, you can see the older app versions for the same apps. Or you can filter Datetime to see a specific timeframe and the apps’ Store presence back then.\n",
    "You can also filter for a specific app using Product Name.\n",
    "If the description is too long, clarisights is not able to display the full description; however, if you select description and current_release_date cells to copy and paste it to a text editor, you'll see the full description.\n",
    "'''\n",
    "\n",
    "for s in sentence.split('\\n'):\n",
    "    print(s)\n",
    "    align, spec, stop_tokens, wav = tts(model, s, CONFIG, use_cuda, ap, use_gl=use_gl, figures=True)\n",
    "    wavs = np.concatenate([wavs, np.zeros(int(ap.sample_rate * 0.5)), wav])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
